/**
 * Copyright(c) by JNet Smartware Group.
 * All rights reserved. Not distribution or redistribution without
 * any permission of JNet Smartware Group.
 * 
 * @author thuc
 * 2010/03/15
 */
package vn.smartware.co.java.net.commons;

import java.math.BigDecimal;
import java.text.Collator;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * common util
 * 
 * @author thuctt
 */
@SuppressWarnings({"rawtypes","unchecked"})
public final class Utils { 

	private static Log log = LogFactory.getLog(Utils.class);
	
	/**
	 * plus date
	 * 
	 * @param date
	 * @param plusYear
	 * @param plusMonth
	 * @param plusDay
	 * @return date
	 */
	public static Date plusDate(Date date, int plusYear, int plusMonth,
			int plusDay) {

		if (date == null) {
			return null;
		}
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.YEAR, plusYear);
		calendar.add(Calendar.MONTH, plusMonth);
		calendar.add(Calendar.DATE, plusDay);
		return calendar.getTime();
	}

	/**
	 * convert to java date object
	 * @param strDatetime
	 * @param pattern
	 * @return Date
	 */
	public static Date parseDate(String strDatetime, String pattern) {
		
		Date date = null;
		try{
			DateFormat dfm = new SimpleDateFormat(pattern);
			date = dfm.parse(strDatetime);
		}catch (ParseException  e) {
			log.info("[ERROR]{Utils-toDate} ParseException." + e);
		}
		return date;
	}
	
	/**
	 * plus hour/minute/second to a date 
	 * 
	 * @param date
	 * @param plusHour
	 * @param plusMinute
	 * @param plusSecond
	 * @return result date
	 */
	public static Date plusTime(Date date, int plusHour, int plusMinute,
			int plusSecond) {

		if (date == null) {
			return null;
		}
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(Calendar.HOUR_OF_DAY, plusHour);
		calendar.add(Calendar.MINUTE, plusMinute);
		calendar.add(Calendar.SECOND, plusSecond);
		return calendar.getTime();
	}

	/**
	 * format a date with a specified input pattern
	 * 
	 * @param iDate
	 * @param pattern
	 * @return String
	 */
	public static String formatDate(Date iDate, String pattern) {

		if (iDate == null) {
			return null;
		}
		DateFormat dateFormat = new SimpleDateFormat(pattern);
		return dateFormat.format(iDate);
	}
	
	/**
	 * get today (YYYY/MM/DD)
	 * 
	 * @return String
	 */
	public static String today() {

		return formatDate(new Date(), "yyyy/MM/dd");
	}
	
	/**
	 * get now (HH:MM)
	 * 
	 * @return String
	 */
	public static String now() {

		return formatDate(new Date(), "HH:mm");
	}
	
	/**
	 * format a date in SHORT date format
	 * 
	 * @param iDate
	 * @return String
	 */
	public static String formatDateShortPattern(Date iDate) {

		if (iDate == null) {
			return null;
		}
		DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
		String pattern = ((SimpleDateFormat) df).toPattern();
		DateFormat dateFormat = new SimpleDateFormat(pattern);
		return dateFormat.format(iDate);
	}
	
	/**
	 * format a date in LONG date format
	 * 
	 * @param iDate
	 * @return String
	 */
	public static String formatDateLongPattern(Date iDate) {

		if (iDate == null) {
			return null;
		}
		DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault());
		String pattern = ((SimpleDateFormat) df).toPattern();
		DateFormat dateFormat = new SimpleDateFormat(pattern);
		return dateFormat.format(iDate);
	}
	
	/**
	 * format a date in LONG time format
	 * 
	 * @param iDate
	 * @return String The time formatted string
	 */
	public static String formatTimeLongPattern(Date iDate) {

		if (iDate == null) {
			return null;
		}
		DateFormat df = DateFormat.getTimeInstance(DateFormat.LONG, Locale.getDefault());
		String pattern = ((SimpleDateFormat) df).toPattern();
		DateFormat dateFormat = new SimpleDateFormat(pattern);
		return dateFormat.format(iDate);
	}
	
	/**
	 * format a date in SHORT time format
	 * 
	 * @param iDate
	 * @return String The time formatted string
	 */
	public static String formatTimeShortPattern(Date iDate) {

		if (iDate == null) {
			return null;
		}
		DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault());
		String pattern = ((SimpleDateFormat) df).toPattern();
		DateFormat dateFormat = new SimpleDateFormat(pattern);
		return dateFormat.format(iDate);
	}

	/**
	 * get day of week
	 * 
	 * @param date
	 * @return day string of week
	 */
	public static String getDayOfWeek(Date date) {

		if (date == null) {
			return "";
		}
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		int idx = calendar.get(Calendar.DAY_OF_WEEK);
		switch (idx) {
		case 1:
			return "Sunday";
		case 2:
			return "Monday";
		case 3:
			return "Tuesday";
		case 4:
			return "Wednesday";
		case 5:
			return "Thursday";
		case 6:
			return "Friday";
		case 7:
			return "Saturday";
		default:
			break;
		}
		return "";
	}

	/**
	 * NVL function
	 * 
	 * @param txt
	 * @param repTxt
	 * @return String
	 */
	public static String nvl(String txt, String repTxt) {

		if (null == txt || "".equals(txt.trim())) {
			return repTxt;
		}
		return txt.trim();
	}

	/**
	 * convert to integer from a string
	 * 
	 * @param numStr
	 * @return int
	 */
	public static int toInt(String numStr) {

		if (null == numStr || "".equals(numStr.trim())) {
			return 0;
		}
		String tmp = numStr.replaceAll("\\+", "");// delete all char "+"
		return Integer.parseInt(tmp.trim());
	}

	/**
	 * check null or empty of an object
	 * 
	 * @param obj
	 * @return boolean
	 */
	public static boolean isNullEmpty(Object obj) {
		
		if (null == obj){
			return true;
		}
		
		if ("".equals(obj.toString().trim())) {
			return true;
		}
		return false;
	}

	/**
	 * add a number of char into left of input string
	 * 
	 * @param iStr
	 * @param addedChar
	 * @param totalChar
	 * @return String
	 */
	public static String addLeft(String iStr, String addedChar, int totalChar) {

		if (iStr.length() >= totalChar) {
			return iStr;
		}
		String tmp = "";
		for (int i = 0; i < totalChar - iStr.length(); i++) {
			tmp += addedChar;
		}
		return tmp + iStr;
	}

	/**
	 * trim an input string
	 * 
	 * @param txt
	 * @return String
	 */
	public static String toEmpty(Object txt) {
		
		if (isNullEmpty(txt)) {
			return "";
		}
		return txt.toString().trim();
	}

	/**
	 * convert String to Date in pattern format
	 * @param dateStr
	 * @param pattern
	 * @return boolean
	 */
	public static boolean isValidDate(String dateStr, String pattern){
		try{
			if (dateStr != null && dateStr.length() > 0) {
				DateFormat dateFormat = new SimpleDateFormat(pattern);
				dateFormat.setLenient(false);
				
				dateFormat.parse(dateStr);			
			}
		}catch (ParseException e){
			return false;
		}	
		return true;
	}
	
	/**
	 * check numeric
	 * @param obj
	 * @return boolean
	 */
	public static boolean isNumeric(String obj){
		if (Utils.isNullEmpty(obj)){
			return true;
		}
		return obj.matches("((-|\\+)?[0-9]+(\\.[0-9]+)?)+");
	}
	
	/**
	 * get max length
	 * @param txt
	 * @return int
	 */
	public static int maxLength(Object txt){
		
		if (null == txt){
			return 0;
		}
		String str = toEmpty(txt);
		return str.length();
	}
	
	/**
	 * check number halfsize
	 * @param obj
	 * @return false if error, else true
	 */
	public static boolean isHalfsize(Object obj) {
		
		if (null == obj){
			return false;
		}
		String str = Utils.toEmpty(obj.toString());
		return str.getBytes().length == str.length();
	}
	
	/**
	 * check full size
	 * 
	 * @param obj
	 * @return false if error, else true
	 */
	public static boolean isFullSize(Object obj) {
		
		if (null == obj) {
			return false;
		}
		String str = Utils.toEmpty(obj);
		return str.getBytes().length == str.length() * 2;
	}
	
	/**
	 * check max length of object
	 * @param obj
	 * @param length
	 * @return false if error, else true
	 */
	public static boolean checkMaxlength(Object obj, long length) {
		
		if (null == obj){
			return true;
		}
		return obj.toString().getBytes().length <= length;
	}
	
	/**
	 * 
	 * @param strDate 
	 * @param plus 
	 * @return String
	 */
	public static String plusDate(String strDate, int plus) {
		
		//check null
		if (strDate == null) {
			return null;
		}
		
		String ret = strDate + "";
		
		int strLength = ret.length();
		if (strLength == 7) {
			ret = ret + "/01";
		}
		
		DateFormat dateFormat = DateFormat.getDateInstance();
		try {
			Date date = dateFormat.parse(ret);
			
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(date);
			calendar.add(Calendar.MONTH, plus);
			
			date = calendar.getTime();
			
			ret = dateFormat.format(date);
			
			if (strLength == 7) {
				ret = ret.substring(0, 7);
			}
			
			return ret;
			
		} catch (ParseException e) {
			log.info("[ERROR] ParseException. " + e);
			return strDate;
		}
		
	}
	
	/**
	 * plusDate
	 * @param strDate 
	 * @param plusYear
	 * @param plusMonth
	 * @param plusDay
	 * @return String
	 */
	public static String plusDate(String strDate, int plusYear, int plusMonth, int plusDay) {
		
		if (strDate == null) {
			return null;
		}
		
		String ret = strDate + "";
		
		int strLength = ret.length();
		if (strLength == 7) {
			ret = ret + "/01";
		}
		
		DateFormat dateFormat = DateFormat.getDateInstance();
		try {
			Date date = dateFormat.parse(ret);
			
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(date);
			calendar.add(Calendar.YEAR, plusYear);
			calendar.add(Calendar.MONTH, plusMonth);
			//
			if (strLength != 7) {
				calendar.add(Calendar.DATE, plusDay);
			}
			
			date = calendar.getTime();
			
			ret = dateFormat.format(date);
			
			if (strLength == 7) {
				ret = ret.substring(0, 7);
			}
			
			return ret;
			
		} catch (ParseException e) {
			log.info("[ERROR] ParseException. " + e);
			return strDate;
		}
	}
	
	/**
	 * compareYYYYMM
	 * @param date1
	 * @param date2
	 * @param equal
	 * @return boolean
	 */
	public static boolean compareYYYYMM(String date1, String date2, boolean equal) {
		
		if (date1.length() != 7 && date1.length() != 10
				|| date2.length() != 7 && date2.length() != 10) {
			return false;
		}
		
		date1 = date1.substring(0, 7);
		date2 = date2.substring(0, 7);
		
		date1 = date1.replaceAll("/", "");
		date2 = date2.replaceAll("/", "");
		
		int dateNum1 = Integer.parseInt(date1);
		int dateNum2 = Integer.parseInt(date2);
		
		if (equal == true) {
			if (dateNum1 >= dateNum2) {
				return true;
			}
			return false;
			
		}
		if (dateNum1 > dateNum2) {
			return true;
		}
		return false;
	}
	
	
	/**
	 * compareYYYYMMDD
	 * @param date1
	 * @param date2
	 * @param equal
	 * @return boolean
	 */
	public static boolean compareYYYYMMDD(String date1, String date2, boolean equal) {
		
		if (date1.length() != 7 && date1.length() != 10
				|| date2.length() != 7 && date2.length() != 10) {
			return false;
		}
		
		if (date1.length() == 7) {
			date1 = date1 + "/01";
		}
		if (date2.length() == 7) {
			date2 = date2 + "/01";
		}
		
		date1 = date1.replaceAll("/", "");
		date2 = date2.replaceAll("/", "");
		
		int dateNum1 = Integer.parseInt(date1);
		int dateNum2 = Integer.parseInt(date2);
		
		if (equal == true) {
			if (dateNum1 >= dateNum2) {
				return true;
			}
			return false;
			
		}
		if (dateNum1 > dateNum2) {
			return true;
		}
		return false;
	}

	/**
	 * add double quotation
	 * @param txt
	 * @return String
	 */
	public static String addDoubleQuotations(String txt) {

		if (isNullEmpty(txt)){
			return "";
		}
		return Const.DOUBLE_QUOTATION + txt + Const.DOUBLE_QUOTATION;
	}

	/**
	 * format input string to YYYY/MM
	 * @param iDate
	 * @return String
	 */
	public static String formatYYYYMM(Date iDate) {
		
		if (iDate == null) {
			return null;
		}
		DateFormat dateFormat = new SimpleDateFormat("yyyy/MM");
		return dateFormat.format(iDate);
	}
	
	/**
	 * format input string to YYYY/MM/DD
	 * @param iDate
	 * @return String
	 */
	public static String formatYYYYMMDD(Date iDate) {
		
		if (iDate == null) {
			return null;
		}
		DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
		return dateFormat.format(iDate);
	}
	
	/**
	 * Return a <code>String</code> object which separated by
	 * <code>separator</code>
	 * 
	 * @param list
	 *            list of elements
	 * @param separator
	 * @return String
	 */
	public static String listToTokens(List list, String separator) {
		StringBuffer buff = new StringBuffer();
		for (int i = 0; list != null && !list.isEmpty() && i < list.size(); i++) {
			if (i != 0) {
				buff.append(separator + list.get(i).toString());
			} else {
				buff.append(list.get(i).toString());
			}
		}
		return buff.toString();
	}
	
	/**
	 * Return list of tokens, using the result of <code>getTokeSep()</code> as
	 * the separator. Blanks are trimmed from tokens.
	 * 
	 * @param tokens
	 *            The string to tokenize into an array
	 * @param separator
	 * @return List
	 */
	public static List tokensToList(String tokens, String separator) {
		if (null==tokens){
			return new ArrayList();
		}
		StringTokenizer tokenizer = new StringTokenizer(tokens, separator);
		List list = new ArrayList(tokenizer.countTokens());
		while (tokenizer.hasMoreTokens()) {
			String token = tokenizer.nextToken().trim();
			if ((token == null) || (token.length() == 0))
				continue;
			list.add(token);
		}
		return list;
	} // end tokensToList
	
	/**
	 * get display country list
	 * @param hasBlank 
	 * @return String[]
	 */
	public static String[] getCountryList(boolean hasBlank){
		
		List countries = new ArrayList();
		if (hasBlank){
			countries.add("");
		}
		Locale[] locales = Locale.getAvailableLocales();
		for (int i = 0; i < locales.length; i++) {
			String name = locales[i].getDisplayCountry();
			if (!isNullEmpty(name)){
				countries.add(name);
			}
		}
		Collections.sort(countries, new StringComparator());
		
		ArrayList retLs = new ArrayList();
		int index = 0;
		for (int i = 0; i < countries.size(); i++) {
			String item = (String)countries.get(i);
			if (!isNullEmpty(item)){
				index++;
				retLs.add(Integer.toString(index) + Const.COMBO_SEPERATE + item);
			}else{
				retLs.add(item);
			}
		}
		
		return (String[]) retLs.toArray(new String[retLs.size()]);
	}
	
	/**
	 * StringComparator: for sorting only
	 * @author thuc
	 *
	 */
	private static class StringComparator implements Comparator {

		private Comparator comparator;

		/**
		 * constructor
		 *
		 */
		public StringComparator() {
			comparator = Collator.getInstance();
		}
		  
		/**
		 * @param arg0 
		 * @param arg1 
		 * @return int
		 */
		public int compare(Object arg0, Object arg1) {
			return comparator.compare(arg0, arg1);
		}
	}
	
	/**
	 * isNormalChars
	 * @param argStr
	 * @return boolean
	 */
	public static boolean isAlphaNummericOnly(String argStr) {
		
		String txt = toEmpty(argStr);
		
		return txt.matches("^[a-zA-Z0-9]+$");
	}

	
	/**
	 * isNormalChars
	 * @param argStr
	 * @return boolean
	 */
	public static boolean isCharactersOnly(String argStr) {
		
		String txt = toEmpty(argStr);
		
		return txt.matches("^[a-zA-Z]+$");
	}
	
	/**
	 * format a number to #000,000,000 pattern
	 * @param argStr
	 * @return String
	 */
	public static String formatNumber(String argStr) {
		
		if (isNullEmpty(argStr)){
			return "";
		}
		
		NumberFormat formatter = new DecimalFormat("###,###,###");
		return formatter.format(Long.parseLong(argStr));
	}
	
	/**
	 * format a number to #000,000,000.00 pattern
	 * @param argStr
	 * @return String
	 */
	public static String formatDouble(String argStr) {
		
		if (isNullEmpty(argStr)){
			return "";
		}
		
		NumberFormat formatter = new DecimalFormat("#,###,###.#");
		return formatter.format(Double.parseDouble(argStr));
	}
	
	/**
	 * remove ',' from a number formatted by #000,000,000 pattern
	 * @param argStr
	 * @return String
	 */
	public static String toNormalNumber(String argStr) {
		
		if (isNullEmpty(argStr)){
			return "";
		}
		
		return argStr.replaceAll(",", "");
	}

	/**
	 * compareHHMM (HH:MM)
	 * @param text1
	 * @param text2
	 * @param equal
	 * @return true if text1 >= text2, otherwise, return false
	 */
	public static boolean compareHHMM(String text1, String text2, boolean equal) {
		
		Date date1 = Utils.parseDate(text1, "HH:mm");
		Date date2 = Utils.parseDate(text2, "HH:mm");
		if (equal){
			if (date1.compareTo(date2) >= 0){
				return true;
			}else{
				return false;
			}
		}else{
			if (date1.compareTo(date2) > 0){
				return true;
			}else{
				return false;
			}
		}
	}
	
	/**
	 * round
	 * @param d
	 * @param decimalPlace
	 * @return double
	 */
	public static double round(double d, int decimalPlace){
	    // see the Javadoc about why we use a String in the constructor
	    // http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#BigDecimal(double)
	    BigDecimal bd = new BigDecimal(Double.toString(d));
	    bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP);
	    return bd.doubleValue();
	  }

	/**
	 * Convert Java Date to SQL Date
	 * @param javaDate
	 * @return java.sql.Date
	 */
	public static java.sql.Date parseSQLDate(Date javaDate) {
		if (javaDate == null) {
			return null;
		}
		return new java.sql.Date(javaDate.getTime());
	}

	/**
	 * Convert Java Date to SQL Timestamp
	 * @param javaDate
	 * @return java.sql.Timestamp
	 */
	public static java.sql.Timestamp parseSQLTimestamp(Date javaDate) {
		if (javaDate == null) {
			return null;
		}
		return new java.sql.Timestamp(javaDate.getTime());
	}
}
